package bsh;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Map;

import bsh.dyn.Callsite;
import bsh.dyn.Invocable;

/**
 * All compiled scripts (and currently, their inner classes) must implement this interface, but the implementation is usually provided
 * (either by extending {@link Prototype} or due to the compiler injecting them directly). Scripts may also override any of
 * {@link CompiledScript}'s method by creating a method with identical signature (but it's not really recomended to do so).
 * 
 * @author takeshi
 */
public interface CompiledScript extends Serializable {

    /**
     * Sets all the fields on this object with values from the givem {@link Map}
     * 
     * @param map {@link Map}
     */
    void setFields(Map<String, Object> map);

    /**
     * Resets all fields to their default values
     */
    void resetAllFields();

    /**
     * Resets a field to its default value
     * 
     * @param name the name of the field
     */
    void resetField(String name);

    /**
     * Sets a field value
     * 
     * @param name {@link String}
     * @param value {@link Object}
     */
    void setField(String name, Object value);

    /**
     * Returs a field value
     * 
     * @param name String
     * @return {@link Object}
     */
    Object getFieldValue(String name);

    /**
     * Actually executes the script
     * 
     * @return Object
     * @throws Exception if an exception is thrown
     */
    Object executeScript() throws Exception;

    /**
     * Some of the fields are 'dynamic' - they don't show up as class fields. This method returns the value of the field with the given name
     * 
     * @param name String
     * @return Object
     */
    Object getDynamicField(String name);

    /**
     * Assigns a value to a field that is dynamic
     * 
     * @param name the name
     * @param value the value
     */
    void setDynamicField(String name, Object value);

    /**
     * Instantiates an object, searching its constructors for a match. Follows the standard java rules for method invocation
     * 
     * @param callsiteId the callsiteId. A number that represents (uniquely) this callsite. Must be automatically generated.
     * @param type the type to be instantiated
     * @param args the arguments to the constructor
     * @return Object (is never <code>null</code>)
     * @throws Throwable the exception
     */
    Object createObject(int callsiteId, Class<?> type, Object[] args) throws Throwable;

    /**
     * Invokes a method that matches the given arguments. Follows the standard java rules for method invocation.
     * 
     * @param callsiteId the callsiteId. A number that represents (uniquely) this callsite. Must be automatically generated.
     * @param invocationTarget the invocation target. May be a Class to invoke static methods from the given class
     * @param methodName the method name
     * @param args the arguments
     * @return return value if any.
     * @throws Throwable the thrown exception
     */
    Object invokeDynamic(int callsiteId, Object invocationTarget, String methodName, Object[] args) throws Throwable;

    /**
     * Each class must have a static invocation cache, in order to cache callsites and the corresponding method. Implementors may also
     * return new maps to disable the cache. It is highly recomended that the returned map is thread-safe
     * 
     * @return {@link Map}
     */
    Map<Callsite, Invocable> getInvocationCache();

    /**
     * @return the {@link MathContext} associated with this script instance. Is used to transform values to and from {@link BigDecimal}
     * instances and also to perform operations on {@link BigDecimal}
     */
    MathContext getMathContext();

    /**
     * Sets the {@link MathContext} associated with this script instance
     * 
     * @param mathContext {@link MathContext}
     */
    void setMathContext(MathContext mathContext);

    /**
     * @return the flags used to compile this class
     */
    int debugFlags();

    /**
     * Converts a double value to a {@link BigDecimal} using this script's {@link #getMathContext()}
     * 
     * @param value double
     * @return {@link BigDecimal}
     */
    BigDecimal localToBigDecimal(double value);

    /**
     * Debug method
     */
    void popStack();

    /**
     * Debug method
     */
    void popMethod();

    /**
     * Sets a local variable value
     * 
     * @param name String
     * @param value {@link Object}
     */
    void setLocalVariable(String name, Object value);

    /**
     * Debug method
     * 
     * @param name {@link String}
     */
    void newStackFrame(String name);

    /**
     * Clears only the dynamic fields
     */
    void clearDynamicFields();

}
